#include<bits/stdc++.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/time.h>

#define THREAD_COUNT (16)
namespace fastIO {
#define BUF_SIZE 100000
#define OUT_SIZE 100000
#define ll long long

    struct Ostream_fwrite {
        char *buf, *p1, *pend;

        Ostream_fwrite() {
            buf = new char[BUF_SIZE];
            p1 = buf;
            pend = buf + BUF_SIZE;
        }

        void out(char ch) {
            if (p1 == pend) {
                fwrite(buf, 1, BUF_SIZE, stdout);
                p1 = buf;
            }
            *p1++ = ch;
        }

        void print(int x) {
            static char s[15], *s1;
            s1 = s;
            if (!x)*s1++ = '0';
            if (x < 0)out('-'), x = -x;
            while (x)*s1++ = x % 10 + '0', x /= 10;
            while (s1-- != s)out(*s1);
        }

        void print(char *s) { while (*s)out(*s++); }

        void println(char *s) {
            while (*s)out(*s++);
            out('\n');
        }

        void flush() {
            if (p1 != buf) {
                fwrite(buf, 1, p1 - buf, stdout);
                p1 = buf;
            }
        }

        ~Ostream_fwrite() { flush(); }
    } Ostream;

    inline void println(int x) {
        Ostream.print(x);
        Ostream.out('\n');
    }

    inline void print(char *s) { Ostream.print(s); }

    inline void println(char *s) { Ostream.println(s); }

#undef ll
#undef OUT_SIZE
#undef BUF_SIZE
};
using namespace fastIO;

using namespace std;
const int N = 3e6 + 5;
const int M = 2e5 + 5;
const char numchar[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

int G1[M][55];
int G2[M][55];

bool has[M];
string num_string[M];

//void prtime(string pos, timeval &time) {
//    printf("%s=", pos.c_str());
//    struct timeval now;
//    gettimeofday(&now, NULL);
//    printf("%dms\n", int((now.tv_sec * 1000 + now.tv_usec / 1000) - (time.tv_sec * 1000 + time.tv_usec / 1000)));
//    time = now;
//}

int t_len[M];

struct find_huan {
    int is[M], f[M], num, cur[10], rt, xx[M];
    bool flag[M];
//    string path[5];
    char path[5][10000000];
    int len[5];
    int G[M][55];

    inline void add_ans(int now_len) {
        ++num;
        int j = now_len - 3;
        char *ss = path[j];
        for (int i = 1; i <= now_len; i++) {
            strcat(ss, (num_string[cur[i]]).c_str());
            len[j] += num_string[cur[i]].size();
        }
        ss[len[j]-1] = '\n';
    }

    void add_huan(int u, int step) {
//        flag[u] = true;
        for(int i = 1; i <= G[u][0]; i++) {
            int v = G[u][i];
            if(flag[v]) continue;
            cur[step + 1] = v;
            add_ans(step + 1);
        }
//        flag[u] = false;
    }

    void dfs(int u, int step) {
        int stp = step + 1;

        for (int i = 1; i <= G1[u][0]; i++) {
            int v = G1[u][i];
            if (v < rt || flag[v] || (stp > 4 && is[v] != rt)) continue;
            cur[stp] = v;
            if (stp >= 3 && f[v] == rt) add_ans(stp);
            if(stp == 6) {
                if(xx[v] == rt) add_huan(v, stp);
                continue;
            }
            flag[v] = true;
            dfs(v, stp);
            flag[v] = false;
        }
    }

    void dfs_ni(int u, int step) {
        if(step == 3 && xx[u] != rt) {
            xx[u] = rt; G[u][0] = 0;
            for (int i = 1; i <= G1[u][0]; i++) {
                if(f[G1[u][i]] == rt) {
                    G[u][++G[u][0]] = G1[u][i];
                }
            }
        }

        for (int i = 1; i <= G2[u][0]; i++) {
            int v = G2[u][i];
            if (v < rt || flag[v]) continue;
            is[v] = rt;
            if (step == 3) continue;
            flag[v] = true;
            dfs_ni(v, step + 1);
            flag[v] = false;
        }
    }

    void start(int l, int r) {
        if (l > r) return;
//        init();
        for (int i = l; i <= r; i++) {
            rt = i;
            flag[i] = true;
            cur[1] = i;
            for (int j = 1; j <= G2[i][0]; j++) {
                if(G2[i][j] > rt)
                    f[G2[i][j]] = rt;
            }
            dfs_ni(i, 1);
            dfs(i, 1);
            flag[i] = false;
        }
    }
} T[THREAD_COUNT];


void find_thread(int s, int t, int k) {
//    timeval time;
//    gettimeofday(&time, NULL);
    T[k].start(s, t);
//    prtime("找环线程[" + to_string(k) + "]结束 " + "s = " + to_string(s) + " t=" + to_string(t) + "  ", time);
}

inline string numtostring(int x) {
    string tmp = ",";
    if (x == 0) {
        return "0";
    }
    while (x != 0) {
        tmp += numchar[x % 10];
        x /= 10;
    }
    reverse(tmp.begin(), tmp.end());
    return tmp;
}

struct node_edge {
    int u, v;
} p3[560005];

int mp[M];

void add_edge(int u, int v) {
    G1[u][++G1[u][0]] = v;
    G2[v][++G2[v][0]] = u;
}

int main() {
//    timeval start;
//    gettimeofday(&start, NULL);
//    timeval time;
//    gettimeofday(&time, NULL);
//    char *inputfile = "/data/test_data.txt";
//    char *outputfile = "/projects/student/output.txt";
    char *inputfile = "./data/1004812/test_data.txt";
//    char *inputfile = "./data/2861665/test_data.txt";
//    char *inputfile = "./data/2609044/test_data.txt";
//    char *inputfile = "./data/2.in";
    char *outputfile = "./z1.out";
    freopen("w", outputfile, stdout);

    int m = 0, n = 0;
    //mmap读文件开始
    char *data = NULL;
    int fd = open(inputfile, O_RDONLY);
    long size = lseek(fd, 0, SEEK_END);
    data = (char *) mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
    int u, v, cost;
    int tmp = 0;
    int ii = 0, jj = 0;
    int nodenum = 0;
    while (data[ii] != '\0') {
        if (data[ii] >= '0' && data[ii] <= '9') {
            tmp *= 10;
            tmp += data[ii] - '0';
        } else if (data[ii] == ',') {
            if (jj == 0) {
                u = tmp;
                jj++;
            } else if (jj == 1) {
                v = tmp;
                if (!has[u])
                    has[u] = true, n = max(n, u);
                if (!has[v])
                    has[v] = true, n = max(n, v);
                p3[++m] = {u, v};
            }
            tmp = 0;
        } else {
            jj = 0;
            tmp = 0;
        }
        ++ii;
    }
    int c_m = 0;
    for (int i = 0; i <= n; i++) {
        if (has[i]) {
            mp[i] = ++c_m;
            num_string[c_m] = numtostring(i);
        }
    }
    for (int i = 1; i <= m; i++) {
        add_edge(mp[p3[i].u], mp[p3[i].v]);
    }
    n = c_m;
    for (int i = 1; i <= n; i++) {
        sort(G1[i] + 1, G1[i] + G1[i][0] + 1);
    }
//    prtime("读数据加建图", time);

    //负载均衡2
    int fid = 0;
    int splitNum = n / 200;
    int count[20] = {2, 2, 2, 2,
                     2, 2, 3, 3,
                     3, 3, 4, 7,
                     7, 11, 23};

//    int count[20] = {3, 3, 3, 3,
//                     3, 3, 4, 4,
//                     4, 4, 5, 8,
//                     9, 50, 22};
    std::thread threads[THREAD_COUNT];
    int s = 1;
    for(int i = 0; i <= 14; i++) {
        count[i] *= splitNum;
        threads[i] = std::thread(find_thread, s, s + count[i] -1, i);
        s += count[i];
    }
    threads[15] = std::thread(find_thread, s, n, 15);
    for (int i = 0; i < THREAD_COUNT; ++i)
        threads[i].join();


    int num = 0;
    for (int i = 0; i < THREAD_COUNT; ++i) num += T[i].num;

    println(num);
    for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < THREAD_COUNT; ++j) {
            print(T[j].path[i]);
        }
    }



//    prtime("总时间 ", start);
//    printf("%d\n", num);
    return 0;
}